﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Collections.ObjectModel;
using System.ServiceModel.Description;
using System.Configuration;
using System.ServiceModel.Channels;
using System.IO;
using System.Xml;
using System.Threading;
using dasBlog.Storage.Configuration;
using newtelligence.DasBlog.Runtime;

namespace dasBlog.Storage.Test
{
    class Program
    {
        static void Main(string[] args)
        {
            TestContextUris();
            TestTextStorage("test.dasblog.info", 5);
            StressTestTextStorage();

            TestServices();
            TestRuntime();
        }

        static void TestRuntime()
        {
            Runtime.Start();

            IBlogDataService dataService = BlogDataServiceFactory.GetService();

            Runtime.Stop();
        }

        static void StressTestTextStorage()
        {
            int numThreads = 64;
            WaitHandle[] mrs = new ManualResetEvent[numThreads];
            for (int thrd = 0; thrd < numThreads; thrd++)
            {
                int t = thrd;
                mrs[t] = new ManualResetEvent(false);
                ThreadPool.QueueUserWorkItem(new WaitCallback(
                    delegate(object o) 
                    {
                        TestTextStorage("scope" + t.ToString(), 10);
                        ((ManualResetEvent)mrs[t]).Set();
                    }));
            }
            WaitHandle.WaitAll(mrs);

            TestContextUris();
            TestServices();
        }

        private static void TestContextUris()
        {
            Moniker contextUri1 = new Moniker("names://foo");
            if (!(contextUri1.Scope == "foo" &&
                  contextUri1.Segments.Count == 0))
                throw new InvalidOperationException();

            Moniker contextUri2 = new Moniker("names://foo/posts");
            if (!(contextUri2.Scope == "foo" &&
                  contextUri2.Segments.Count == 1 &&
                  contextUri2.Segments[0].Name.Equals(PathSegmentName.Posts) &&
                  contextUri2.Segments[0].Id == null &&
                  contextUri2.ItemId == null))
                throw new InvalidOperationException();

            Moniker contextUri3 = new Moniker("names://foo/posts/");
            if (!(contextUri3.Scope == "foo" &&
                  contextUri3.Segments.Count == 1 &&
                  contextUri3.Segments[0].Name == PathSegmentName.Posts &&
                  contextUri3.Segments[0].Id == null &&
                  contextUri3.ItemId == null))
                throw new InvalidOperationException();

            Moniker contextUri4 = new Moniker("names://foo/posts/1/foo");
            if (!(contextUri4.Scope == "foo" &&
                  contextUri4.Segments.Count == 2 &&
                  contextUri4.Segments[0].Name == PathSegmentName.Posts &&
                  contextUri4.Segments[0].Id == "1" &&
                  contextUri4.Segments[1].Name == new PathSegmentName("foo")))
                throw new InvalidOperationException();

            Moniker contextUri5 = new Moniker("names://foo/posts/2/foo/");
            Moniker contextUri6 = new Moniker("names://foo/posts/2/comments/");
            Moniker contextUri7 = new Moniker("names://foo/posts/2/comments/2");
        }

        static void Main2(string[] args)
        {
            StorageBusClient.StorageBusClient remoteStorageBus = new dasBlog.Storage.Test.StorageBusClient.StorageBusClient();
            remoteStorageBus.ChannelFactory.Endpoint.Behaviors.Add(new SimpleAuthenticationBehavior());
            remoteStorageBus.ClientCredentials.UserName.UserName = "admin";
            remoteStorageBus.ClientCredentials.UserName.Password = "!adminadmin";
            
            var storageNode = new dasBlog.Storage.SqlServer.TextEntryStorageProvider();

            ServiceHost host = new ServiceHost(StorageNodeFactory.CreateStorageNode(typeof(dasBlog.Storage.SqlServer.TextEntryStorageProvider)), new Uri("http://localhost/foobar"));
            host.Description.Behaviors.Add(new ServiceMetadataBehavior { HttpGetEnabled = true });
            var storageEndpoint = host.AddServiceEndpoint(typeof(IStorageNode), new BasicHttpBinding(BasicHttpSecurityMode.None), "");
            host.Open();


            Moniker context = new Moniker("foo", PathSegmentName.Posts);
            TextEntry textEntry = new TextEntry { Title = "Test", Content = "Hello" };
            textEntry.Categories.Add("foo");
            textEntry.Categories.Add("bar");
            storageNode.Store(context, textEntry);

            Console.WriteLine("Ready");
            Console.ReadLine();
            
        }

        private static void TestTextStorage(string scopeid, int numItems)
        {
            Random rnd = new Random();
            DateTime stopWatch;
            TimeSpan stopWatchResult;
            Moniker context = new Moniker(scopeid, PathSegmentName.Posts);
            List<Moniker> entries = new List<Moniker>();

            var storageProvider = new dasBlog.Storage.SqlServer.TextEntryStorageProvider();

            Console.Write("Adding {0} items", numItems);
            stopWatch = DateTime.Now;
            for (int i = 0; i < numItems; i++)
            {
                TextEntry textEntry = new TextEntry { Title = "Test", Content = "Hello "+i.ToString() };
                textEntry.Id = context;
                textEntry.Created = new DateTime(2007, 1, 1).AddDays(rnd.Next(365));
                if ( i % 2 == 0 ) textEntry.Categories.Add("foo");
                if ( i % 2 == 1 ) textEntry.Categories.Add("bar");
                entries.Add(storageProvider.Store(textEntry.Id, textEntry));
            }
            stopWatchResult = DateTime.Now - stopWatch;
            Console.WriteLine(" in {0} seconds, {1} items per second", stopWatchResult.TotalMilliseconds / 1000d, numItems / (double)stopWatchResult.TotalSeconds);

            Console.Write("Adding comments to {0} items", entries.Count);
            stopWatch = DateTime.Now;
            foreach( Moniker uri in entries )
            {
                TextEntry textEntry = new TextEntry { Title = "Comment", Content = uri.ToString() };
                textEntry.Id = new Moniker(uri, PathSegmentName.Comments);
                storageProvider.Store( textEntry.Id, textEntry);                
            }
            stopWatchResult = DateTime.Now - stopWatch;
            Console.WriteLine(" in {0} seconds, {1} items per second", stopWatchResult.TotalMilliseconds / 1000d, entries.Count() / (double)stopWatchResult.TotalSeconds);

            stopWatch = DateTime.Now;
            Console.Write("Stored Posts: {0}", storageProvider.Select(context, MonikerMatch.Exact, null).Count());
            stopWatchResult = DateTime.Now - stopWatch;
            Console.WriteLine(" retrieved in {0} seconds", stopWatchResult.TotalMilliseconds / 1000d);

            stopWatch = DateTime.Now;
            Console.Write("Stored Items: {0}", storageProvider.Select(context, MonikerMatch.Prefix, null).Count());
            stopWatchResult = DateTime.Now - stopWatch;
            Console.WriteLine(" retrieved in {0} seconds", stopWatchResult.TotalMilliseconds / 1000d);

            stopWatch = DateTime.Now;
            Console.Write("Tags: {0}", storageProvider.Aggregate(context, MonikerMatch.Exact, "tag").Count());
            stopWatchResult = DateTime.Now - stopWatch;
            Console.WriteLine(" retrieved in {0} seconds", stopWatchResult.TotalMilliseconds / 1000d);

            stopWatch = DateTime.Now;
            Console.Write("Stored Items with 'foo' tag: {0}", storageProvider.Select(context, MonikerMatch.Exact, new TagQueryDescription("foo")).Count());
            stopWatchResult = DateTime.Now - stopWatch;
            Console.WriteLine(" retrieved in {0} seconds", stopWatchResult.TotalMilliseconds / 1000d);

            stopWatch = DateTime.Now;
            Console.Write("Stored Items with 'bar' tag: {0}", storageProvider.Select(context, MonikerMatch.Exact, new TagQueryDescription("bar")).Count());
            stopWatchResult = DateTime.Now - stopWatch;
            Console.WriteLine(" retrieved in {0} seconds", stopWatchResult.TotalMilliseconds / 1000d);

            stopWatch = DateTime.Now;
            Console.Write("Stored Items in May 2007: {0}", storageProvider.Select(context, MonikerMatch.Exact, new DateRangeQueryDescription(new DateTime(2007, 5, 1), new DateTime(2007, 5, 31))).Count());
            stopWatchResult = DateTime.Now - stopWatch;
            Console.WriteLine(" retrieved in {0} seconds", stopWatchResult.TotalMilliseconds / 1000d);

            stopWatch = DateTime.Now;
            Console.Write("Removing items and verifying ");
            numItems = 0;
            foreach (var a in storageProvider.Select(context, MonikerMatch.Prefix, null))
            {
                numItems++;
                storageProvider.Delete(a.Id);
                if (storageProvider.Get(a.Id) != null)
                    throw new InvalidOperationException();
            }
            stopWatchResult = DateTime.Now - stopWatch;
            Console.WriteLine(" {0} done in {1} seconds, {2} items per second", numItems, stopWatchResult.TotalMilliseconds / 1000d, numItems / (double)stopWatchResult.TotalSeconds);
        }

        public static void CreateBlog(string blogScope, IStorageNode textEntryNode, IStorageNode streamNode)
        {
            StorageBus bus = StorageBus.Current;

            var posts = new NodeDescription(textEntryNode);
            var pictures = new NodeDescription(streamNode);
            var media = new NodeDescription(streamNode);

            posts.Paths.Add(PathSegmentName.Comments, posts);
            posts.Paths.Add(PathSegmentName.Annotations, posts);
            posts.Paths.Add(PathSegmentName.Media, media);
            posts.Paths.Add(PathSegmentName.Pictures, pictures);

            pictures.Paths.Add(PathSegmentName.Comments, posts);
            pictures.Paths.Add(PathSegmentName.Annotations, posts);

            media.Paths.Add(PathSegmentName.Comments, posts);
            media.Paths.Add(PathSegmentName.Annotations, posts);

            ScopeDescription scope = new ScopeDescription(blogScope);
            scope.Paths.Add(PathSegmentName.Posts, posts);
            scope.Paths.Add(PathSegmentName.Pictures, pictures);
            scope.Paths.Add(PathSegmentName.Media, media);
            bus.AddStorageScope(scope);
        }

        private static void TestServices()
        {
            Collection<Uri> baseAddresses;
            Collection<ServiceHost> hosts = new Collection<ServiceHost>();

            baseAddresses = new Collection<Uri>();
            baseAddresses.Add(new Uri("http://localhost/storage/"));
            baseAddresses.Add(new Uri("net.pipe://localhost/storage/"));
            baseAddresses.Add(new Uri("net.tcp://localhost/storage/"));
            string blogScope = "test.dasblog.info";

            var webXmlBaseAddresses = from Uri u in baseAddresses where u.Scheme == Uri.UriSchemeHttp || u.Scheme == Uri.UriSchemeHttps select u;
            var webXmlSvcHost = new WebServiceHost(typeof(WebStorageNode), webXmlBaseAddresses.ToArray<Uri>());
            webXmlSvcHost.AddServiceEndpoint(typeof(IWebStorageNode), new WebHttpBinding(), "");
            webXmlSvcHost.Open();

            List<Moniker> entries = new List<Moniker>();            
            for (int i = 0; i < 10; i++)
            {
                TextEntry blogEntry = new TextEntry();
                blogEntry.Title = "title" + i.ToString();
                blogEntry.Content = "foobar"+i.ToString();
                blogEntry.Categories.Add("foo");
                Moniker entryId = new Moniker(blogScope, PathSegmentName.Posts).Store<TextEntry>(blogEntry);
                entries.Add(entryId);

                for (int j = 0; j < 5; j++)
                {
                    TextEntry comment = new TextEntry();
                    comment.Title = "title" + i.ToString()+j.ToString();
                    comment.Content = " foobar" + i.ToString() + j.ToString();
                    comment.Parent = entryId.ToString();
                    new Moniker(entryId, PathSegmentName.Comments).Store<TextEntry>(comment);
                }
            }

            foreach( string fileName in Directory.GetFiles(Environment.GetFolderPath(System.Environment.SpecialFolder.MyPictures)))
            {
                IStreamStorageNode node = StorageBus.Current.FindNode(new Moniker(blogScope,PathSegmentName.Media)) as IStreamStorageNode;
                if (node != null)
                {
                    var result = node.SetStream(new StreamStorageSetRequest
                    {
                        moniker = new Moniker(blogScope, PathSegmentName.Media),
                        categories = new string[]{"foo","bar"},
                        Stream = new FileStream(fileName, FileMode.Open,FileAccess.Read, FileShare.Read),
                        name = Path.GetFileName(fileName) }
                    );
                    Console.WriteLine(result.StreamInfo.Size);
                }
            }


            //System.Net.WebRequest wr = System.Net.WebRequest.Create(string.Format("http://localhost/storage/{0}/{1}/", blogScope, PathSegmentName.Posts.Value));
            //var response = wr.GetResponse();
            //var list = SerializationTools<TextEntry>.DeserializeList(new XmlTextReader(response.GetResponseStream()));

            //int matches = (
            //    from TextEntry te in list
            //    from Moniker u in entries
            //    where new Moniker(te.Id).Equals(u)
            //    select new { te.Id, u }).Count();
            //Console.WriteLine(matches);

            //wr = System.Net.WebRequest.Create(new Moniker(list[0].Id).ToUri(new Uri("http://localhost/storage/")));
            //response = wr.GetResponse();

        }
    }
}
